[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