[pypy-svn] r8836 - in pypy/dist/pypy: annotation interpreter translator translator/test

arigo at codespeak.net arigo at codespeak.net
Fri Feb 4 13:41:54 CET 2005


Author: arigo
Date: Thu Feb  3 18:13:45 2005
New Revision: 8836

Modified:
   pypy/dist/pypy/annotation/bookkeeper.py
   pypy/dist/pypy/interpreter/argument.py
   pypy/dist/pypy/translator/genc.h
   pypy/dist/pypy/translator/genpyrex.py
   pypy/dist/pypy/translator/test/snippet.py
   pypy/dist/pypy/translator/test/test_ctrans.py
   pypy/dist/pypy/translator/test/test_pyrextrans.py
Log:
- modified the Argument class to unpack ** arguments lazily too, just like *
  arguments.  This allows the flow object space to handle ** arguments in
  calls.

- updated genc.h accordingly to accept and decode ** arguments between the C
  functions.

- removed the skip_on_error() in test_ctrans -- these ones mask real errors,
  and skipping should be restricted on "no C compiler found" kind of errors.

- genpyrex.py probably works, but writing a test for that is slightly messy.
  Still, a proto-test found a previous bug in genpyrex.py.



Modified: pypy/dist/pypy/annotation/bookkeeper.py
==============================================================================
--- pypy/dist/pypy/annotation/bookkeeper.py	(original)
+++ pypy/dist/pypy/annotation/bookkeeper.py	Thu Feb  3 18:13:45 2005
@@ -226,7 +226,7 @@
 
         elif func.func_code.co_flags & CO_VARARGS:
             # calls to *arg functions: create one version per number of args
-            assert not args.kwds_w, (
+            assert not args.has_keywords(), (
                 "keyword forbidden in calls to *arg functions")
             nbargs = len(args.arguments_w)
             if args.w_stararg is not None:

Modified: pypy/dist/pypy/interpreter/argument.py
==============================================================================
--- pypy/dist/pypy/interpreter/argument.py	(original)
+++ pypy/dist/pypy/interpreter/argument.py	Thu Feb  3 18:13:45 2005
@@ -22,25 +22,7 @@
         self.arguments_w = list(args_w)
         self.kwds_w = kwds_w.copy()
         self.w_stararg = w_stararg
-        if w_starstararg is not None:
-            # unlike the * argument we unpack the ** argument immediately.
-            # 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("the keywords must be "
-                                                "a dictionary"))
-            for w_key in space.unpackiterable(w_starstararg):
-                try:
-                    key = space.str_w(w_key)
-                except OperationError:
-                    raise OperationError(space.w_TypeError,
-                                         space.wrap("keywords must be strings"))
-                if key in self.kwds_w:
-                    raise OperationError(self.space.w_TypeError,
-                                         self.space.wrap("got multiple values "
-                                                         "for keyword argument "
-                                                         "'%s'" % key))
-                self.kwds_w[key] = space.getitem(w_starstararg, w_key)
+        self.w_starstararg = w_starstararg
 
     def frompacked(space, w_args=None, w_kwds=None):
         """Convenience static method to build an Arguments
@@ -49,6 +31,11 @@
     frompacked = staticmethod(frompacked)
 
     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,)
@@ -63,22 +50,52 @@
 
     def unpack(self):
         "Return a ([w1,w2...], {'kw':w3...}) pair."
+        # --- unpack the * argument now ---
         if self.w_stararg is not None:
             self.arguments_w += self.space.unpackiterable(self.w_stararg)
             self.w_stararg = None
+        # --- unpack the ** argument now ---
+        if self.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("the keywords must be "
+                                                "a dictionary"))
+            d = self.kwds_w.copy()   # don't change the original yet,
+                                     # in case something goes wrong
+            for w_key in space.unpackiterable(w_starstararg):
+                try:
+                    key = space.str_w(w_key)
+                except OperationError:
+                    raise OperationError(space.w_TypeError,
+                                         space.wrap("keywords must be strings"))
+                if key in d:
+                    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
         return self.arguments_w, self.kwds_w
 
     def prepend(self, w_firstarg):
         "Return a new Arguments with a new argument inserted first."
         args =  Arguments(self.space, [w_firstarg] + self.arguments_w,
-                          self.kwds_w, self.w_stararg)
+                          self.kwds_w, self.w_stararg, self.w_starstararg)
         args.blind_arguments = self.blind_arguments + 1
         return args
 
+    def has_keywords(self):
+        return self.kwds_w or (self.w_starstararg is not None and
+                               self.space.is_true(self.w_starstararg))
+
     def fixedunpack(self, argcount):
         """The simplest argument parsing: get the 'argcount' arguments,
         or raise a real ValueError if the length is wrong."""
-        if self.kwds_w:
+        if self.has_keywords():
             raise ValueError, "no keyword arguments expected"
         if len(self.arguments_w) > argcount:
             raise ValueError, "too many arguments (%d expected)" % argcount
@@ -143,6 +160,10 @@
                 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
         kwds_w = self.kwds_w
 
@@ -198,21 +219,32 @@
         shape_cnt  = len(self.arguments_w)        # Number of positional args
         shape_keys = self.kwds_w.keys()           # List of keywords (strings)
         shape_star = self.w_stararg is not None   # Flag: presence of *arg
+        shape_stst = self.w_starstararg is not None # Flag: presence of **kwds
         data_w = self.arguments_w + [self.kwds_w[key] for key in shape_keys]
         if shape_star:
             data_w.append(self.w_stararg)
-        return (shape_cnt, tuple(shape_keys), shape_star), data_w
+        if shape_stst:
+            data_w.append(self.w_starstararg)
+        return (shape_cnt, tuple(shape_keys), shape_star, shape_stst), data_w
 
-    def fromshape(space, (shape_cnt, shape_keys, shape_star), data_w):
+    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[shape_cnt+i]
+            kwds_w[shape_keys[i]] = data_w[p]
+            p += 1
         if shape_star:
-            w_star = data_w[-1]
+            w_star = data_w[p]
+            p += 1
         else:
             w_star = None
-        return Arguments(space, args_w, kwds_w, w_star)
+        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)
     # XXX the "shape" tuple should be considered as a black box from
     #     other code, but translator/genc.h examines it.

Modified: pypy/dist/pypy/translator/genc.h
==============================================================================
--- pypy/dist/pypy/translator/genc.h	(original)
+++ pypy/dist/pypy/translator/genc.h	Thu Feb  3 18:13:45 2005
@@ -470,19 +470,21 @@
 	PyObject* o;
 	PyObject* key;
 	PyObject* t2;
-	int i, nargs, nkwds, nvarargs, starflag;
+	int i, nargs, nkwds, nvarargs, starflag, starstarflag;
 	va_list vargs;
 
 	if (!PyTuple_Check(shape) ||
-	    PyTuple_GET_SIZE(shape) != 3 ||
+	    PyTuple_GET_SIZE(shape) != 4 ||
 	    !PyInt_Check(PyTuple_GET_ITEM(shape, 0)) ||
 	    !PyTuple_Check(PyTuple_GET_ITEM(shape, 1)) ||
-	    !PyInt_Check(PyTuple_GET_ITEM(shape, 2))) {
+	    !PyInt_Check(PyTuple_GET_ITEM(shape, 2)) ||
+	    !PyInt_Check(PyTuple_GET_ITEM(shape, 3))) {
 		Py_FatalError("in genc.h: invalid 'shape' argument");
 	}
 	nargs = PyInt_AS_LONG(PyTuple_GET_ITEM(shape, 0));
 	nkwds = PyTuple_GET_SIZE(PyTuple_GET_ITEM(shape, 1));
 	starflag = PyInt_AS_LONG(PyTuple_GET_ITEM(shape, 2));
+	starstarflag = PyInt_AS_LONG(PyTuple_GET_ITEM(shape, 3));
 
 	va_start(vargs, shape);
 	t = PyTuple_New(nargs);
@@ -516,6 +518,22 @@
 		if (t == NULL)
 			goto finally;
 	}
+	if (starstarflag) {
+		int len1, len2, len3;
+		o = va_arg(vargs, PyObject *);
+		len1 = PyDict_Size(d);
+		len2 = PyDict_Size(o);
+		if (len1 < 0 || len2 < 0)
+			goto finally;
+		if (PyDict_Update(d, o) < 0)
+			goto finally;
+		len3 = PyDict_Size(d);
+		if (len1 + len2 != len3) {
+			PyErr_SetString(PyExc_TypeError,
+					"genc.h: duplicate keyword arguments");
+			goto finally;
+		}
+	}
 	va_end(vargs);
 
 	result = PyObject_Call(callable, t, d);

Modified: pypy/dist/pypy/translator/genpyrex.py
==============================================================================
--- pypy/dist/pypy/translator/genpyrex.py	(original)
+++ pypy/dist/pypy/translator/genpyrex.py	Thu Feb  3 18:13:45 2005
@@ -114,10 +114,12 @@
         shape = self.op.args[1].value
         args = Arguments.fromshape(None, shape, a[2:])
         lst = args.arguments_w[:]
-        for key, value in args.kwds_w:
+        for key, value in args.kwds_w.items():
             lst.append("%s=%s" % (key, value))
         if args.w_stararg is not None:
             lst.append("*%s" % args.w_stararg)
+        if args.w_starstararg is not None:
+            lst.append("**%s" % args.w_starstararg)
         return "%s = %s(%s)" % (self.resultname, a[0], ", ".join(lst))
 
     def op_simple_call(self):

Modified: pypy/dist/pypy/translator/test/snippet.py
==============================================================================
--- pypy/dist/pypy/translator/test/snippet.py	(original)
+++ pypy/dist/pypy/translator/test/snippet.py	Thu Feb  3 18:13:45 2005
@@ -540,6 +540,9 @@
 def call_with_keyword(z):
     return default_args(-20, z=z)
 
+def call_very_complex(z, args, kwds):
+    return default_args(-20, z=z, *args, **kwds)
+
 def powerset(setsize=int):
     """Powerset
 

Modified: pypy/dist/pypy/translator/test/test_ctrans.py
==============================================================================
--- pypy/dist/pypy/translator/test/test_ctrans.py	(original)
+++ pypy/dist/pypy/translator/test/test_ctrans.py	Thu Feb  3 18:13:45 2005
@@ -15,12 +15,14 @@
 class TestNoTypeCGenTestCase:
     objspacename = 'flow'
 
-    def build_cfunc(self, func):
+    def build_cfunc(self, func, *morefuncs):
         try: func = func.im_func
         except AttributeError: pass
         t = Translator(func)
+        for fn in morefuncs:
+            t.getflowgraph(fn)
         t.simplify()
-        return py.test.skip_on_error(t.ccompile) 
+        return t.ccompile()
 
     def test_simple_func(self):
         cfunc = self.build_cfunc(snippet.simple_func)
@@ -153,6 +155,13 @@
         call_with_keyword = self.build_cfunc(snippet.call_with_keyword)
         assert call_with_keyword(100) == 82
 
+    def test_call_very_complex(self):
+        call_very_complex = self.build_cfunc(snippet.call_very_complex,
+                                             snippet.default_args)
+        assert call_very_complex(5, (3,), {}) == -12
+        assert call_very_complex(5, (), {'y': 3}) == -12
+        raises(TypeError, call_very_complex, 5, (3,), {'y': 4})
+
     def test_finallys(self):
         finallys = self.build_cfunc(snippet.finallys)
         assert finallys(['hello']) == 8
@@ -196,7 +205,7 @@
                 argstypelist.append(spec)
         a = t.annotate(argstypelist)
         a.simplify()
-        return py.test.skip_on_error(t.ccompile) 
+        return t.ccompile()
 
     def test_set_attr(self):
         set_attr = self.getcompiled(snippet.set_attr)

Modified: pypy/dist/pypy/translator/test/test_pyrextrans.py
==============================================================================
--- pypy/dist/pypy/translator/test/test_pyrextrans.py	(original)
+++ pypy/dist/pypy/translator/test/test_pyrextrans.py	Thu Feb  3 18:13:45 2005
@@ -91,6 +91,15 @@
         assert sand(0, 6) == "no"
         assert sand(0, 0) == "no"
 
+# -- the following test doesn't really work right now --
+##    def test_call_very_complex(self):
+##        call_very_complex = self.build_cfunc(snippet.call_very_complex,
+##                                             snippet.default_args)
+##        assert call_very_complex(5, (3,), {}) == -12
+##        assert call_very_complex(5, (), {'y': 3}) == -12
+##        py.test.raises("call_very_complex(5, (3,), {'y': 4})")
+
+
 class TestTypedTestCase:
 
     def getcompiled(self, func):



More information about the Pypy-commit mailing list