[pypy-svn] rev 1121 - in pypy/branch/builtinrefactor/pypy: interpreter module module/test

hpk at codespeak.net hpk at codespeak.net
Sat Jul 12 11:50:54 CEST 2003


Author: hpk
Date: Sat Jul 12 11:50:52 2003
New Revision: 1121

Removed:
   pypy/branch/builtinrefactor/pypy/interpreter/pycode_app.py
   pypy/branch/builtinrefactor/pypy/interpreter/pyframe_app.py
Modified:
   pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py
   pypy/branch/builtinrefactor/pypy/interpreter/pycode.py
   pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py
   pypy/branch/builtinrefactor/pypy/module/builtin.py
   pypy/branch/builtinrefactor/pypy/module/test/test_builtin.py
Log:
- got rid of pyframe_app, and pyframe_app by introducing a new 
  mechanism for *exposing app-level defined methods at interp-level*.
  This is not really perfect but i begins to work so i check it in.
  the mechanism is invoked like so (it's not perfect, i know)

  class someclass:

    def app_method(self, fixed, number, of, args):
        # do app-level stuff, don't use 'self' for the time beeing
        # examples: decode_code_arguments, normalize_exception
        
    method = app2interp(app_method)

    def othermethod(self):
        # 'self.space' *must* contain an objectspace instance
        self.method(fixed, number, of, args)

- there is a new (experimental, of course!) approach to executing
  code objects.  Actually the "app2interp" method when called
  makes up an "AppBuiltinCode" instance which is a simple form
  of a code object.  Instead of the whole "build_arguments" to-dict-locals-
  and-then-get-it-out-of-locals business it constructs arguments 
  in a straightforward manner.  Thus it's currently not possible 
  to pass anything like the exact number of arguments (no defaults, etc.)
  It probably makes sense to extend argument-parsing by using Michael's 
  extmodule's subclassing technique for handling various argument-passing
  cases. 

  Not that AppBuiltinCode creates a new kind of frame called "AppFrame"
  which subclass PyFrame but has an easy initialization (see above)

- moved tests to applevel and added one for execfile



Modified: pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py	(original)
+++ pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py	Sat Jul 12 11:50:52 2003
@@ -1,6 +1,5 @@
 from executioncontext import ExecutionContext, OperationError, NoValue
-import pyframe, threadlocals
-import pypy.module.builtin
+import threadlocals
 
 __all__ = ['ObjSpace', 'OperationError', 'NoValue', 'PyPyError']
 
@@ -22,6 +21,7 @@
         self.initialize()
 
     def make_builtins(self):
+        import pypy.module.builtin
         self.builtin = pypy.module.builtin.Builtin(self)
         self.w_builtin = self.builtin.wrap_base()
         self.w_builtins = self.getattr(self.w_builtin, self.wrap("__dict__"))

Modified: pypy/branch/builtinrefactor/pypy/interpreter/pycode.py
==============================================================================
--- pypy/branch/builtinrefactor/pypy/interpreter/pycode.py	(original)
+++ pypy/branch/builtinrefactor/pypy/interpreter/pycode.py	Sat Jul 12 11:50:52 2003
@@ -16,15 +16,36 @@
 # look at this if it makes sense
 # think of a proper base class???
 
-import baseobjspace, pyframe, executioncontext
-import appfile
-
-appfile = appfile.AppFile(__name__, ["interpreter"])
+import baseobjspace, executioncontext
 
 CO_VARARGS     = 0x0004
 CO_VARKEYWORDS = 0x0008
 
+class app2interp(object):
+    """ this class exposes an app-level defined function at interpreter-level 
+       
+        Assumption:  the interp-level function will be called ala
+
+                            a.function(arg1, arg2, argn)
+
+                     with 'a' having an attribute 'space' which the app-level 
+                     code should run in. (might change in during the branch)
+    """
+
+    def __init__(self, func):
+        #print "making app2interp for", func
+        self.func = func
+        self._codecache = {}
 
+    def __get__(self, instance, cls=None):
+        space = instance.space
+        try:
+            return self._codecache[(space, instance, self)] 
+        except KeyError:
+            c = AppBuiltinCode(space, self.func, instance)
+            self._codecache[(space, instance, self)] = c
+            return c
+        
 class PyBaseCode(object):
     def __init__(self):
         self.co_name = ""
@@ -59,16 +80,104 @@
         if w_defaults is None: w_defaults = space.newtuple([])
         if w_closure  is None: w_closure  = space.newtuple([])
         w_bytecode = space.wrap(co)
-        w_arguments = space.gethelper(appfile).call(
-            "decode_code_arguments", [w_arguments, w_kwargs, w_defaults,
-                                      w_bytecode])
-        # we assume that decode_code_arguments() gives us a dictionary
-        # of the correct length.
+
+        self.space = space
+        w_locals = self.decode_code_arguments(w_arguments, w_kwargs, 
+                                         w_defaults, w_bytecode)
         if space.is_true(w_closure):
             l = zip(co.co_freevars, space.unpackiterable(w_closure))
             for key, w_cell in l:
-                space.setitem(w_arguments, space.wrap(key), w_cell)
-        return w_arguments
+                space.setitem(w_locals, space.wrap(key), w_cell)
+        return w_locals
+
+    def app_decode_code_arguments(self, args, kws, defs, codeobject):
+        """
+        Assumptions:
+        args       sequence of the normal actual parameters
+        kws        dictionary of keyword actual parameters
+        defs       sequence of defaults
+        codeobject our code object carrying argument info
+        """
+        CO_VARARGS = 0x4
+        CO_VARKEYWORDS = 0x8
+        varargs = (codeobject.co_flags & CO_VARARGS) and 1
+        varkeywords = (codeobject.co_flags & CO_VARKEYWORDS) and 1
+        varargs_tuple = ()
+
+        argdict = {}
+        parameter_names = codeobject.co_varnames[:codeobject.co_argcount]
+
+        # Normal arguments
+        for i in range(0, len(args), 1):    # see comment above for ", 1"
+            if 0 <= i < len(parameter_names): # try
+                argdict[parameter_names[i]] = args[i]
+            else: # except IndexError:
+                # If varargs, put in tuple, else throw error
+                if varargs:
+                    varargs_tuple = args[i:]
+                else:
+                    raise TypeError, 'Too many parameters to callable object'
+                break
+
+        # Put all suitable keywords into arglist
+        if kws:
+            if varkeywords:
+                # Allow all keywords
+                newkw = {}
+                for key in kws.keys():
+                    for name in parameter_names:
+                        if name == key:
+                            if key in argdict:
+                                raise TypeError, 'Setting parameter %s twice.' % name
+                            else:
+                                argdict[key] = kws[key]
+                            break # name found in parameter names
+                    else:
+                        newkw[key] = kws[key]
+
+            else:
+                # Only allow formal parameter keywords
+                count = len(kws)
+                for name in parameter_names:
+                    if name in kws:
+                        count -= 1
+                        if name in argdict:
+                            raise TypeError, 'Setting parameter %s twice.' % name
+                        else:
+                            argdict[name] = kws[name]
+                if count:
+                    # XXX This should be improved to show the parameters that
+                    #     shouldn't be here.
+                    raise TypeError('Setting keyword parameter that does '
+                                    'not exist in formal parameter list.')
+        else:
+            newkw = {}
+
+        # Fill in with defaults, starting at argcount - defcount
+        if defs:
+            argcount = codeobject.co_argcount
+            defcount = len(defs)
+            for i in range(argcount - defcount, argcount, 1): # ", 1" comment above
+                if parameter_names[i] in argdict:
+                    continue
+                argdict[parameter_names[i]] = defs[i - (argcount - defcount)]
+
+        if len(argdict) < codeobject.co_argcount:
+            raise TypeError, 'Too few parameters to callable object'
+
+        namepos = codeobject.co_argcount
+        if varargs:
+            name = codeobject.co_varnames[namepos]
+            argdict[name] = varargs_tuple
+            namepos += 1
+        if varkeywords:
+            name = codeobject.co_varnames[namepos]
+            argdict[name] = newkw
+
+        return argdict
+
+    decode_code_arguments = app2interp(app_decode_code_arguments)
+
         
 class PyByteCode(PyBaseCode):
     """Represents a code object for Python functions.
@@ -115,12 +224,14 @@
         self.co_consts = newconsts
 
     def eval_code(self, space, w_globals, w_locals):
+        from pypy.interpreter import pyframe
         frame = pyframe.PyFrame(space, self, w_globals, w_locals)
         ec = space.getexecutioncontext()
         w_ret = ec.eval_frame(frame)
         return w_ret
 
     def locals2cells(self, space, w_locals):
+        from pypy.interpreter import pyframe
         localcells = []
         Cell = pyframe.Cell
         for name in self.co_varnames:
@@ -145,3 +256,35 @@
             cell = space.unwrap(w_cell)
             nestedcells.append(cell)
         return localcells, nestedcells
+
+class AppBuiltinCode:
+    """The code object implementing a app-level hook """
+
+    def __init__(self, space, func, instance=None):
+        assert func.func_code.co_flags & (CO_VARARGS|CO_VARKEYWORDS) == 0
+        self.space = space
+
+        #PyBaseCode.__init__(self)
+        co = func.func_code
+
+        self.instance = instance
+        self.func = func
+        self.co_code = co.co_code
+        self.co_name = func.__name__
+        self.co_consts = co.co_consts
+        self.co_flags = co.co_flags
+        self.co_varnames = tuple(co.co_varnames)
+        self.co_nlocals = co.co_nlocals
+        self.co_argcount = co.co_argcount 
+        self.co_names = co.co_names
+        self.next_arg = self.co_argcount 
+
+    def __call__(self, *args_w):
+        from pypy.interpreter import pyframe
+        w_globals = self.space.newdict([])
+        if self.instance:
+            args_w = (self.space.wrap(self.instance),) + args_w  # effects untested
+        frame = pyframe.AppFrame(self.space, self, w_globals, args_w)
+        ec = self.space.getexecutioncontext()
+        w_ret = ec.eval_frame(frame)
+        return w_ret

Deleted: pypy/branch/builtinrefactor/pypy/interpreter/pycode_app.py
==============================================================================
--- pypy/branch/builtinrefactor/pypy/interpreter/pycode_app.py	Sat Jul 12 11:50:52 2003
+++ (empty file)
@@ -1,99 +0,0 @@
-# replacement for decode_frame_arguments
-#
-# ===== ATTENTION =====
-#
-# This code is pretty fundamental to pypy and great care must be taken
-# to avoid infinite recursion.  In particular:
-#
-# - All calls here must be "easy", i.e. not involve default or keyword
-#   arguments.  For example, all range() calls need three arguments.
-#
-# - You cannot *catch* any exceptions (raising is fine).
-#
-# (I wonder if the pain of writing this at interpreter level might be
-# worth it...)
-
-def decode_code_arguments(args, kws, defs, codeobject):
-    """
-    Assumptions:
-    args = sequence of the normal actual parameters
-    kws = dictionary of keyword actual parameters
-    defs = sequence of defaults
-    """
-    CO_VARARGS = 0x4
-    CO_VARKEYWORDS = 0x8
-    varargs = (codeobject.co_flags & CO_VARARGS) and 1
-    varkeywords = (codeobject.co_flags & CO_VARKEYWORDS) and 1
-    varargs_tuple = ()
-    
-    argdict = {}
-    parameter_names = codeobject.co_varnames[:codeobject.co_argcount]
-    
-    # Normal arguments
-    for i in range(0, len(args), 1):    # see comment above for ", 1"
-        if 0 <= i < len(parameter_names): # try
-            argdict[parameter_names[i]] = args[i]
-        else: # except IndexError:
-            # If varargs, put in tuple, else throw error
-            if varargs:
-                varargs_tuple = args[i:]
-            else:
-                raise TypeError, 'Too many parameters to callable object'
-            break
-
-    # Put all suitable keywords into arglist
-    if kws:
-        if varkeywords:
-            # Allow all keywords
-            newkw = {}
-            for key in kws.keys():
-                for name in parameter_names:
-                    if name == key:
-                        if key in argdict:
-                            raise TypeError, 'Setting parameter %s twice.' % name
-                        else:
-                            argdict[key] = kws[key]
-                        break # name found in parameter names
-                else:
-                    newkw[key] = kws[key]
-                    
-        else:
-            # Only allow formal parameter keywords
-            count = len(kws)
-            for name in parameter_names:
-                if name in kws:
-                    count -= 1
-                    if name in argdict:
-                        raise TypeError, 'Setting parameter %s twice.' % name
-                    else:
-                        argdict[name] = kws[name]
-            if count:
-                # XXX This should be improved to show the parameters that
-                #     shouldn't be here.
-                raise TypeError('Setting keyword parameter that does '
-                                'not exist in formal parameter list.')
-    else:
-        newkw = {}
-                
-    # Fill in with defaults, starting at argcount - defcount
-    if defs:
-        argcount = codeobject.co_argcount
-        defcount = len(defs)
-        for i in range(argcount - defcount, argcount, 1): # ", 1" comment above
-            if parameter_names[i] in argdict:
-                continue
-            argdict[parameter_names[i]] = defs[i - (argcount - defcount)]
-
-    if len(argdict) < codeobject.co_argcount:
-        raise TypeError, 'Too few parameters to callable object'
-
-    namepos = codeobject.co_argcount
-    if varargs:
-        name = codeobject.co_varnames[namepos]
-        argdict[name] = varargs_tuple
-        namepos += 1
-    if varkeywords:
-        name = codeobject.co_varnames[namepos]
-        argdict[name] = newkw
-
-    return argdict

Modified: pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py
==============================================================================
--- pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py	(original)
+++ pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py	Sat Jul 12 11:50:52 2003
@@ -2,9 +2,8 @@
 """
 
 from pypy.interpreter.executioncontext import OperationError, Stack, NoValue
-from pypy.interpreter.appfile import AppFile
 
-appfile = AppFile(__name__, ["interpreter"])
+from pypy.interpreter.pycode import app2interp
 
 
 class PyFrame:
@@ -229,8 +228,8 @@
 ##             import pdb
 ##             pdb.set_trace()
 ##             print w_type, `w_value`, frame.bytecode.co_name
-            w_res = s.gethelper(appfile).call(
-                "normalize_exception", [w_type, w_value])
+            self.space = s # needed for the following call
+            w_res = self.normalize_exception(w_type, w_value)
             w_value = s.getitem(w_res, s.wrap(1))
             
             frame.valuestack.push(w_value)
@@ -238,6 +237,22 @@
             frame.next_instr = self.handlerposition   # jump to the handler
             raise StopUnrolling
 
+    def app_normalize_exception(self, etype, evalue):
+        # mistakes here usually show up as infinite recursion, which is fun.
+        if isinstance(evalue, etype):
+            return etype, evalue
+        if isinstance(etype, type) and issubclass(etype, Exception):
+            if evalue is None:
+                evalue = ()
+            elif not isinstance(evalue, tuple):
+                evalue = (evalue,)
+            evalue = etype(*evalue)
+        else:
+            raise Exception, "?!"
+        return etype, evalue
+    normalize_exception = app2interp(app_normalize_exception)
+
+
 
 class FinallyBlock(FrameBlock):
     """A try:finally: block.  Stores the position of the exception handler."""
@@ -370,3 +385,41 @@
         else:
             return "%s(%s)" % (self.__class__.__name__, self.w_value)
 
+class AppFrame(PyFrame):
+    """Represents a frame for a regular Python function
+    that needs to be interpreted.
+
+    Public fields:
+     * 'space' is the object space this frame is running in
+     * 'w_locals' is the locals dictionary to use
+     * 'w_globals' is the attached globals dictionary
+     * 'w_builtins' is the attached built-ins dictionary
+     * 'valuestack', 'blockstack', 'next_instr' control the interpretation
+    """
+
+    def __init__(self, space, bytecode, w_globals, argtuple):
+        self.space = space
+        self.bytecode = bytecode # Misnomer; this is really like a code object
+
+        # XXX we may want to have lazy access to interp-level through w_globals later
+        self.w_globals = w_globals
+
+        # XXX construct self.w_locals
+        self.nestedcells = ()
+        self.localcells = [ Cell(x) for x in argtuple ]
+        missing = self.bytecode.co_nlocals - len(self.localcells)
+        self.localcells.extend([ Cell() for x in range(missing)])
+        
+        self.w_builtins = self.load_builtins()
+        self.valuestack = Stack()
+        self.blockstack = Stack()
+        self.last_exception = None
+        self.next_instr = 0
+        #self._dump()
+
+    def _dump(self):
+        print "AppFrame_dump"
+        print "  space     ", self.space
+        print "  localcells", self.localcells
+        print "  co_varnames  ", self.bytecode.co_varnames
+

Deleted: pypy/branch/builtinrefactor/pypy/interpreter/pyframe_app.py
==============================================================================
--- pypy/branch/builtinrefactor/pypy/interpreter/pyframe_app.py	Sat Jul 12 11:50:52 2003
+++ (empty file)
@@ -1,99 +0,0 @@
-# XXX
-# This is deprecated.
-# We use the same functionality,. but do it in
-# pycode_app.py.
-# the function is just fine, we just don't reduce the
-# dictionary; it is the result.
-
-def decode_frame_arguments(args, kws, defs, closure, codeobject):
-    """
-    Assumptions:
-    args = sequence of the normal actual parameters
-    kws = dictionary of keyword actual parameters
-    defs = sequence of defaults
-    """
-    CO_VARARGS = 0x4
-    CO_VARKEYWORDS = 0x8
-    varargs = (codeobject.co_flags & CO_VARARGS) and 1
-    varkeywords = (codeobject.co_flags & CO_VARKEYWORDS) and 1
-    varargs_tuple = ()
-    
-    argdict = {}
-    parameter_names = codeobject.co_varnames[:codeobject.co_argcount]
-    
-    # Normal arguments
-    for i in range(len(args)):
-        try:
-            argdict[parameter_names[i]] = args[i]
-        except IndexError:
-            # If varargs, put in tuple, else throw error
-            if varargs:
-                varargs_tuple = args[i:]
-            else:
-                raise TypeError, 'Too many parameters to callable object'
-
-    # Put all suitable keywords into arglist
-    if kws:
-        if varkeywords:
-            # Allow all keywords
-            newkw = {}
-            for key in kws.keys():
-                for name in parameter_names:
-                    if name == key:
-                        if argdict.has_key(key):
-                            raise TypeError, 'Setting parameter %s twice.' % name
-                        else:
-                            argdict[key] = kws[key]
-                        break # name found in parameter names
-                else:
-                    newkw[key] = kws[key]
-                    
-        else:
-            # Only allow formal parameter keywords
-            count = len(kws)
-            for name in parameter_names:
-                if kws.has_key(name):
-                    count -= 1
-                    if argdict.has_key(name):
-                        raise TypeError, 'Setting parameter %s twice.' % name
-                    else:
-                        argdict[name] = kws[name]
-            if count:
-                # XXX This should be improved to show the parameters that
-                #     shouldn't be here.
-                raise TypeError, 'Setting keyword parameter that does not exist in formal parameter list.'
-                
-    # Fill in with defaults, starting at argcount - defcount
-    if defs:
-        argcount = codeobject.co_argcount
-        defcount = len(defs)
-        for i in range(argcount - defcount, argcount):
-            if argdict.has_key(parameter_names[i]):
-                continue
-            argdict[parameter_names[i]] = defs[i - (argcount - defcount)]
-
-    if len(argdict) < codeobject.co_argcount:
-        raise TypeError, 'Too few paramteres to callable object'
-
-    a = [argdict[name] for name in parameter_names]
-    if varargs:
-        a.append(varargs_tuple)
-    if varkeywords:
-        a.append(newkw)
-    return tuple(a)
-
-def normalize_exception(etype, evalue):
-    # mistakes here usually show up as infinite recursion, which is
-    # fun.
-    if isinstance(evalue, etype):
-        return etype, evalue
-    if isinstance(etype, type) and issubclass(etype, Exception):
-        if evalue is None:
-            evalue = ()
-        elif not isinstance(evalue, tuple):
-            evalue = (evalue,)
-        evalue = etype(*evalue)
-    else:
-        raise Exception, "?!"
-    return etype, evalue
-

Modified: pypy/branch/builtinrefactor/pypy/module/builtin.py
==============================================================================
--- pypy/branch/builtinrefactor/pypy/module/builtin.py	(original)
+++ pypy/branch/builtinrefactor/pypy/module/builtin.py	Sat Jul 12 11:50:52 2003
@@ -9,16 +9,6 @@
 
 class Builtin(BuiltinModule):
     __pythonname__ = '__builtin__'
-    #__appfile__ = appfile.AppFile(__name__, ["module"])
-
-    #__helper_appfile__ = appfile.AppFile('builtin_helper',["module"])
-
-    # temporary hack, until we have a real tuple type for calling
-    #def tuple(self, w_obj):
-    #    lis = self.space.unpackiterable(w_obj)
-    #    w_res = self.space.newtuple(lis)
-    #    return w_res
-    #tuple = appmethod(tuple)
 
     def _actframe(self, index=-1):
         return self.space.getexecutioncontext().framestack.items[index]
@@ -93,13 +83,13 @@
         return space.wrap(res)
     compile = appmethod(compile)
 
-    def execfile(self, w_filename, w_globals, w_locals):
+    def execfile(self, w_filename, w_globals=None, w_locals=None):
         space = self.space
         #XXX why do i have to check against space.w_None instead of None?
         #    above the compile commands *does* check against None
-        if w_globals is None or w_globals is space.w_None:
+        if w_globals is space.w_None:
             w_globals = self._actframe().w_globals
-        if w_locals is None or w_globals is space.w_None: 
+        if w_locals is space.w_None: 
             w_locals = w_globals
 
         filename = space.unwrap(w_filename)
@@ -174,17 +164,14 @@
         return self.space.ord(w_val)
     ord = appmethod(ord)
 
-
     def pow(self, w_val):
         return self.space.pow(w_val)
     pow = appmethod(pow)
 
-
     def repr(self, w_object):
         return self.space.repr(w_object)
     repr = appmethod(repr)
 
-
     def setattr(self, w_object, w_name, w_val):
         return self.space.setattr(w_object, w_name, w_val)
     setattr = appmethod(setattr)

Modified: pypy/branch/builtinrefactor/pypy/module/test/test_builtin.py
==============================================================================
--- pypy/branch/builtinrefactor/pypy/module/test/test_builtin.py	(original)
+++ pypy/branch/builtinrefactor/pypy/module/test/test_builtin.py	Sat Jul 12 11:50:52 2003
@@ -52,13 +52,42 @@
         self.assertEquals(iter_x.next(), 3)
         self.assertRaises(StopIteration, iter_x.next)
 
-class TestCmp(test.TestCase):
-   
     def test_cmp(self):
-       self.failUnless(cmp(9, 9) == 0)
-       self.failUnless(cmp(0,9) < 0)
-       self.failUnless(cmp(9,0) > 0)
- 
+        self.assertEquals(cmp(9,9), 0)
+        self.assert_(cmp(0,9) < 0)
+        self.assert_(cmp(9,0) > 0)
+
+class TestInternal(test.IntTestCase):
+
+    def setUp(self):
+        self.space = space = test.objspace()
+
+    def get_builtin(self, name):
+        w = self.space.wrap
+        w_builtins = self.space.w_builtins
+        w_obj = self.space.getitem(w_builtins, w(name))
+        return w_obj
+   
+    def test_execfile(self):
+        # we need cpython's tempfile currently to test 
+        from tempfile import mktemp
+        fn = mktemp()
+        f = open(fn, 'w')
+        print >>f, "i=42"
+        f.close()
+
+        try:
+            w_execfile = self.get_builtin('execfile')
+            space = self.space
+            w_dict = space.newdict([])
+            self.space.call(w_execfile, space.newtuple([
+                space.wrap(fn), w_dict, space.w_None]), space.newdict([]))
+            w_value = space.getitem(w_dict, space.wrap('i'))
+            self.assertEqual_w(w_value, space.wrap(42))
+        finally:
+            import os
+            os.remove(fn)
+
 if __name__ == '__main__':
     test.main()
  


More information about the Pypy-commit mailing list