[pypy-svn] r8955 - in pypy/branch/dist-simpler-multimethods/pypy/objspace/std: . test

pedronis at codespeak.net pedronis at codespeak.net
Mon Feb 7 16:39:25 CET 2005


Author: pedronis
Date: Mon Feb  7 16:39:25 2005
New Revision: 8955

Modified:
   pypy/branch/dist-simpler-multimethods/pypy/objspace/std/model.py
   pypy/branch/dist-simpler-multimethods/pypy/objspace/std/multimethod.py
   pypy/branch/dist-simpler-multimethods/pypy/objspace/std/objspace.py
   pypy/branch/dist-simpler-multimethods/pypy/objspace/std/stdtypedef.py
   pypy/branch/dist-simpler-multimethods/pypy/objspace/std/test/test_multimethod.py
Log:
- code to get just an expression source string instead of a function from multimethod 
installing

- wrap also space installed mm with FailToImplement -> TypeError conversion

- don't use a function with *args as the catch-all fallback

- both multimethod level and stdtypedef level code to avoid mm prefix clashes which can lead
  to confusing (dispatch) bugs




Modified: pypy/branch/dist-simpler-multimethods/pypy/objspace/std/model.py
==============================================================================
--- pypy/branch/dist-simpler-multimethods/pypy/objspace/std/model.py	(original)
+++ pypy/branch/dist-simpler-multimethods/pypy/objspace/std/model.py	Mon Feb  7 16:39:25 2005
@@ -142,6 +142,7 @@
                 break
         else:
             self.name = operatorsymbol
+
         if extras.get('general__args__', False):
             self.argnames_after = ['__args__']
         if extras.get('w_varargs', False):
@@ -149,6 +150,7 @@
         if extras.get('varargs_w', False):
             self.argnames_after = ['args_w']            
 
-    def install_not_sliced(self, typeorder):
+    def install_not_sliced(self, typeorder, baked_perform_call=True):
         return self.install(prefix = '__mm_' + self.name,
-                list_of_typeorders = [typeorder]*self.arity)
+                list_of_typeorders = [typeorder]*self.arity,
+                baked_perform_call=baked_perform_call)

Modified: pypy/branch/dist-simpler-multimethods/pypy/objspace/std/multimethod.py
==============================================================================
--- pypy/branch/dist-simpler-multimethods/pypy/objspace/std/multimethod.py	(original)
+++ pypy/branch/dist-simpler-multimethods/pypy/objspace/std/multimethod.py	Mon Feb  7 16:39:25 2005
@@ -4,7 +4,7 @@
     pass
 
 
-def well_just_complain(*args):
+def raiseFailedToImplement():
     raise FailedToImplement
 
 
@@ -39,14 +39,23 @@
             types, order)
         lst[order] = function
 
-    def install(self, prefix, list_of_typeorders):
+    def install(self, prefix, list_of_typeorders, baked_perform_call=True):
+        "NOT_RPYTHON: initialization-time only"
+        assert len(list_of_typeorders) == self.arity
+        installer = Installer(self, prefix, list_of_typeorders,
+                              baked_perform_call=baked_perform_call)
+        return installer.install()
+
+    def install_if_not_empty(self, prefix, list_of_typeorders):
         "NOT_RPYTHON: initialization-time only"
         assert len(list_of_typeorders) == self.arity
         installer = Installer(self, prefix, list_of_typeorders)
         if installer.is_empty():
-            return well_just_complain
+            return None
         else:
-            return installer.install()
+            return installer.install()        
+        
+    
 
     # ____________________________________________________________
     # limited dict-like interface to the dispatch table
@@ -84,19 +93,32 @@
 
 class Installer:
 
-    def __init__(self, multimethod, prefix, list_of_typeorders):
+    prefix_memo = {}
+
+    def __init__(self, multimethod, prefix, list_of_typeorders, baked_perform_call=True):
         self.multimethod = multimethod
+        # avoid prefix clashes, user code should supply different prefixes
+        # itself for nice names in tracebacks
+        n = 1
+        while prefix in self.prefix_memo:
+            n += 1
+            prefix = "%s%d" % (prefix,n)
         self.prefix = prefix
+        self.prefix_memo[prefix] = 1
         self.list_of_typeorders = list_of_typeorders
         self.subtree_cache = {}
         self.to_install = []
         self.non_empty = self.build_tree([], multimethod.dispatch_tree)
+
+        self.baked_perform_call = baked_perform_call
+        
         if self.non_empty:
-            self.perform_call = self.build_function(None, prefix+'perform_call',
-                                                    None,
-                                                    [(None, prefix, 0)])
+            perform = [(None, prefix, 0)]
         else:
-            self.perform_call = well_just_complain
+            perform = []
+
+        self.perform_call = self.build_function(None, prefix+'_perform_call',
+                                                None, perform)
 
     def is_empty(self):
         return not self.non_empty
@@ -106,9 +128,9 @@
         #print >> f, '_'*60
         #import pprint
         #pprint.pprint(self.list_of_typeorders, f)
-        for target, funcname, func, source in self.to_install:
+        for target, funcname, func, source, fallback in self.to_install:
             if target is not None:
-                if hasattr(target, funcname) and func is well_just_complain:
+                if hasattr(target, funcname) and fallback:
                     continue
                 #print >> f, target.__name__, funcname
                 #if source:
@@ -192,15 +214,21 @@
                     invent_name(conversion), callargs[to_convert])
             callname = invent_name(call)
             if call_selfarg_index is not None:
-                self.to_install.append((self.multimethod.root_class,
-                                        callname,
-                                        well_just_complain,
-                                        None))
+                # fallback on root_class
+                self.build_function(self.multimethod.root_class,
+                                    callname, call_selfarg_index, [])
                 callname = '%s.%s' % (callargs.pop(call_selfarg_index), callname)
             callargs = (self.multimethod.argnames_before +
                         callargs + self.multimethod.argnames_after)
             bodylines.append('return %s(%s)' % (callname, ', '.join(callargs)))
 
+        fallback = False
+        if not bodylines:
+            miniglobals['raiseFailedToImplement'] = raiseFailedToImplement
+            bodylines = ['return raiseFailedToImplement()']
+            fallback = True
+
+
         # protect all lines apart from the last one by a try:except:
         for i in range(len(bodylines)-2, -1, -1):
             bodylines[i:i+1] = ['try:',
@@ -208,19 +236,25 @@
                                 'except FailedToImplement:',
                                 '    pass']
 
-        # indent mode
-        bodylines = ['    ' + line for line in bodylines]
-
         if func_selfarg_index is not None:
             selfargs = [funcargs.pop(func_selfarg_index)]
         else:
             selfargs = []
         funcargs = (selfargs + self.multimethod.argnames_before +
                     funcargs + self.multimethod.argnames_after)
+
+        if target is None and not self.baked_perform_call:
+            return funcargs, bodylines[0][len('return '):], miniglobals, fallback
+
+        # indent mode
+        bodylines = ['    ' + line for line in bodylines]
+
         bodylines.insert(0, 'def %s(%s):' % (funcname, ', '.join(funcargs)))
         bodylines.append('')
         source = '\n'.join(bodylines)
+        #print source
+        #print "*"*60
         exec source in miniglobals
         func = miniglobals[funcname]
-        self.to_install.append((target, funcname, func, source))
+        self.to_install.append((target, funcname, func, source, fallback))
         return func

Modified: pypy/branch/dist-simpler-multimethods/pypy/objspace/std/objspace.py
==============================================================================
--- pypy/branch/dist-simpler-multimethods/pypy/objspace/std/objspace.py	(original)
+++ pypy/branch/dist-simpler-multimethods/pypy/objspace/std/objspace.py	Mon Feb  7 16:39:25 2005
@@ -36,7 +36,13 @@
         # install all the MultiMethods into the space instance
         for name, mm in self.MM.__dict__.items():
             if isinstance(mm, MultiMethod) and not hasattr(self, name):
-                func = mm.install_not_sliced(self.model.typeorder)
+                exprargs, expr, miniglobals, fallback = (
+                    mm.install_not_sliced(self.model.typeorder, baked_perform_call=False))
+
+                func = stdtypedef.make_perform_trampoline('__mm_'+name,
+                                                          exprargs, expr, miniglobals,
+                                                          mm)
+                
                                                   # e.g. add(space, w_x, w_y)
                 boundmethod = func.__get__(self)  # bind the 'space' argument
                 setattr(self, name, boundmethod)  # store into 'space' instance

Modified: pypy/branch/dist-simpler-multimethods/pypy/objspace/std/stdtypedef.py
==============================================================================
--- pypy/branch/dist-simpler-multimethods/pypy/objspace/std/stdtypedef.py	(original)
+++ pypy/branch/dist-simpler-multimethods/pypy/objspace/std/stdtypedef.py	Mon Feb  7 16:39:25 2005
@@ -2,7 +2,6 @@
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.typedef import TypeDef, GetSetProperty, Member
 from pypy.objspace.std.model import MultiMethod, FailedToImplement
-from pypy.objspace.std.multimethod import well_just_complain
 
 __all__ = ['StdTypeDef', 'newmethod', 'gateway',
            'GetSetProperty', 'Member', 'attrproperty', 'attrproperty_w',
@@ -147,10 +146,10 @@
 ##        return r
 
 
-def sliced_typeorders(typeorder, multimethod, typedef, i):
+def sliced_typeorders(typeorder, multimethod, typedef, i, local=False):
     list_of_typeorders = [typeorder] * multimethod.arity
-    prefix = '__mm_' + multimethod.name
-    if typedef is not None:
+    prefix = '_mm_' + multimethod.name
+    if not local:
         # slice
         sliced_typeorder = {}
         for type, order in typeorder.items():
@@ -164,18 +163,27 @@
                 sliced_typeorder[type] = lst
         list_of_typeorders[i] = sliced_typeorder
         prefix += '_%sS%d' % (typedef.name, i)
+    else:
+        prefix = typedef.name +'_mth'+prefix
     return prefix, list_of_typeorders
 
 def typeerrormsg(space, operatorsymbol, args_w):
-    return space.wrap("XXX insert message here")
-
-def wrap_func_in_trampoline(func, multimethod, selfindex=0):
-    # mess to figure out how to put a gateway around 'func'
+    type_names = [ space.type(w_arg).name for w_arg in args_w ]
+    if len(args_w) > 1:
+        plural = 's'
+    else:
+        plural = ''
+    msg = "unsupported operand type%s for %s (%s)" % (
+                    plural, operatorsymbol,
+                    ', '.join(type_names))    
+    return space.wrap(msg)
+
+def make_perform_trampoline(prefix, exprargs, expr, miniglobals,  multimethod, selfindex=0,
+                            allow_NotImplemented_results=False):
+    # mess to figure out how to put a gateway around executing expr
     argnames = ['_%d'%(i+1) for i in range(multimethod.arity)]
     explicit_argnames = multimethod.extras.get('argnames', [])
     argnames[len(argnames)-len(explicit_argnames):] = explicit_argnames
-    # XXX do something about __call__ and __init__ which still use
-    # XXX packed arguments: w_args, w_kwds instead of *args_w, **kwds_w
     solid_arglist = ['w_'+name for name in argnames]
     wrapper_arglist = solid_arglist[:]
     if multimethod.extras.get('varargs_w', False):
@@ -187,10 +195,9 @@
     if multimethod.extras.get('general__args__', False):
         wrapper_arglist.append('__args__')
 
-    miniglobals = {'perform_call': func,
-                   'OperationError': OperationError,
-                   'FailedToImplement': FailedToImplement,
-                   'typeerrormsg': typeerrormsg}
+    miniglobals.update({ 'OperationError': OperationError,                         
+                         'typeerrormsg': typeerrormsg})
+    
     app_defaults = multimethod.extras.get('defaults', ())
     i = len(argnames) - len(app_defaults)
     wrapper_signature = wrapper_arglist[:]
@@ -202,23 +209,33 @@
 
     wrapper_signature.insert(0, wrapper_signature.pop(selfindex))
     wrapper_sig  = ', '.join(wrapper_signature)
-    wrapper_args = ', '.join(wrapper_arglist)
-    if len(multimethod.specialnames) > 1:
+
+    src = []
+    dest = []
+    for wrapper_arg,expr_arg in zip(['space']+wrapper_arglist, exprargs):
+        if wrapper_arg != expr_arg:
+            src.append(wrapper_arg)
+            dest.append(expr_arg)
+    renaming = ', '.join(dest) +" = "+', '.join(src)
+
+    if allow_NotImplemented_results and len(multimethod.specialnames) > 1:
         # turn FailedToImplement into NotImplemented
-        code = """def trampoline(space, %s):
+        code = """def %s_perform_call(space, %s):
+                      %s
                       try:
-                          return perform_call(space, %s)
+                          return %s
                       except FailedToImplement, e:
                           if e.args:
                               raise OperationError(e.args[0], e.args[1])
                           else:
                               return space.w_NotImplemented
-"""        % (wrapper_sig, wrapper_args)
+"""        % (prefix, wrapper_sig, renaming, expr)
     else:
         # turn FailedToImplement into nice TypeErrors
-        code = """def trampoline(space, %s):
+        code = """def %s_perform_call(space, %s):
+                      %s
                       try:
-                          w_res = perform_call(space, %s)
+                          w_res = %s
                       except FailedToImplement, e:
                           if e.args:
                               raise OperationError(e.args[0], e.args[1])
@@ -228,10 +245,10 @@
                       if w_res is None:
                           w_res = space.w_None
                       return w_res
-"""        % (wrapper_sig, wrapper_args,
+"""        % (prefix, wrapper_sig, renaming, expr,
               multimethod.operatorsymbol, ', '.join(solid_arglist))
     exec code in miniglobals
-    return miniglobals['trampoline']
+    return miniglobals["%s_perform_call" % prefix]
 
 def wrap_trampoline_in_gateway(func, methname, multimethod):
     unwrap_spec = [gateway.ObjSpace] + [gateway.W_Root]*multimethod.arity
@@ -243,7 +260,7 @@
         unwrap_spec.append(gateway.Arguments)
     return gateway.interp2app(func, app_name=methname, unwrap_spec=unwrap_spec)
 
-def slicemultimethod(space, multimethod, typedef, result):
+def slicemultimethod(space, multimethod, typedef, result, local=False):
     from pypy.objspace.std.objecttype import object_typedef
     for i in range(len(multimethod.specialnames)):
         # each MultimethodCode embeds a multimethod
@@ -256,11 +273,14 @@
                 continue
 
         prefix, list_of_typeorders = sliced_typeorders(
-            space.model.typeorder, multimethod, typedef, i)
-        func = multimethod.install(prefix, list_of_typeorders)
-        if func is well_just_complain:
+            space.model.typeorder, multimethod, typedef, i, local=local)
+        exprargs, expr, miniglobals, fallback = multimethod.install(prefix, list_of_typeorders,
+                                                                    baked_perform_call=False)
+        if fallback:
             continue   # skip empty multimethods
-        trampoline = wrap_func_in_trampoline(func, multimethod, i)
+        trampoline = make_perform_trampoline(prefix, exprargs, expr, miniglobals,
+                                             multimethod, i,
+                                             allow_NotImplemented_results=True)
         gw = wrap_trampoline_in_gateway(trampoline, methname, multimethod)
         gw.bound_position = i   # for the check above
         result[methname] = gw
@@ -272,7 +292,7 @@
         slicemultimethod(space, multimethod, typedef, result)
     # import all multimethods defined directly on the type without slicing
     for multimethod in typedef.local_multimethods:
-        slicemultimethod(space, multimethod, None, result)
+        slicemultimethod(space, multimethod, typedef, result, local=True)
     return result
 
 ##class MultimethodCode(eval.Code):

Modified: pypy/branch/dist-simpler-multimethods/pypy/objspace/std/test/test_multimethod.py
==============================================================================
--- pypy/branch/dist-simpler-multimethods/pypy/objspace/std/test/test_multimethod.py	(original)
+++ pypy/branch/dist-simpler-multimethods/pypy/objspace/std/test/test_multimethod.py	Mon Feb  7 16:39:25 2005
@@ -37,6 +37,7 @@
         W_BoolObject: [(W_BoolObject, None), (W_IntObject, delegate_b2i)],
         W_StringObject: [(W_StringObject, None)],
         }
+    mod.typeorder = typeorder
     mod.add1 = add.install('__add', [typeorder, typeorder])
 
 
@@ -63,3 +64,31 @@
     assert add1(space, w_b, w_b) == 'fine'
     raises(FailedToImplement, "add1(space, w_b, w_s)")
     raises(FailedToImplement, "add1(space, w_s, w_b)")
+
+def test_not_baked():
+    add2 = add.install('__add2', [typeorder, typeorder],baked_perform_call=False)
+    assert add2[0] == ['space', 'arg0', 'arg1']
+    assert add2[1] == 'arg0.__add2(space, arg1)'
+    assert isinstance(add2[2], dict)
+    assert not add2[3]
+
+def test_empty():
+    add3_installer = Installer(add, '__add3', [{},{}])
+    assert add3_installer.is_empty()
+    assert len(add3_installer.to_install) == 1
+    assert add3_installer.to_install[0][0] is None
+
+def test_empty_direct():
+    assert not add.install_if_not_empty('__add4', [{},{}])
+
+
+def test_empty_not_baked():
+    add5_installer = Installer(add, '__add5', [{},{}], baked_perform_call=False)
+    assert add5_installer.is_empty()
+    assert len(add5_installer.to_install) == 0
+    add5 = add5_installer.install()
+    assert add5[0] == ['space', 'arg0', 'arg1']
+    assert add5[1] == 'raiseFailedToImplement()'
+    assert isinstance(add5[2], dict)
+    assert add5[3]
+    



More information about the Pypy-commit mailing list