[pypy-svn] rev 1003 - in pypy/trunk/src/pypy: interpreter objspace/std

arigo at codespeak.net arigo at codespeak.net
Mon Jun 23 16:57:23 CEST 2003


Author: arigo
Date: Mon Jun 23 16:57:22 2003
New Revision: 1003

Modified:
   pypy/trunk/src/pypy/interpreter/baseobjspace.py
   pypy/trunk/src/pypy/objspace/std/default.py
   pypy/trunk/src/pypy/objspace/std/objspace.py
   pypy/trunk/src/pypy/objspace/std/typeobject.py
   pypy/trunk/src/pypy/objspace/std/userobject.py
Log:
Clean-up of the interfaces between multimethods and Python special
__xxx__ methods, in both directions.

M    /home/arigo/python/pypy/pypy/trunk/src/pypy/interpreter/baseobjspace.py
	nonzero multimethod removed, next (iterators) made special
M    /home/arigo/python/pypy/pypy/trunk/src/pypy/objspace/std/userobject.py
	properly translate Python results into multimethod-like results
M    /home/arigo/python/pypy/pypy/trunk/src/pypy/objspace/std/default.py
	is_true falls back to len, nonzero removed
M    /home/arigo/python/pypy/pypy/trunk/src/pypy/objspace/std/typeobject.py
	properly export and translate multimethod results to Python
M    /home/arigo/python/pypy/pypy/trunk/src/pypy/objspace/std/objspace.py
	next is now a special multimethod


Modified: pypy/trunk/src/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/trunk/src/pypy/interpreter/baseobjspace.py	(original)
+++ pypy/trunk/src/pypy/interpreter/baseobjspace.py	Mon Jun 23 16:57:22 2003
@@ -195,7 +195,6 @@
     ('and_',            '&',         2, ['__and__', '__rand__']),
     ('or_',             '|',         2, ['__or__', '__ror__']),
     ('xor',             '^',         2, ['__xor__', '__rxor__']),
-    ('nonzero',         'nonzero',   1, ['__nonzero__']),
     ('int',             'int',       1, ['__int__']),
     ('float',           'float',     1, ['__float__']),
     ('inplace_add',     '+=',        2, ['__iadd__']),
@@ -219,7 +218,6 @@
     ('ge',              '>=',        2, ['__ge__', '__le__']),
     ('contains',        'contains',  2, ['__contains__']),
     ('iter',            'iter',      1, ['__iter__']),
-    ('next',            'next',      1, ['next']),  # iterator interface
     ('call',            'call',      3, ['__call__']),
     ('get',             'get',       3, ['__get__']),
     ('set',             'set',       2, ['__set__']),
@@ -288,4 +286,5 @@
 # newslice(w_start,w_stop,w_end) -> w_slice     (w_end may be a real None)
 #               newfunction(...) -> w_function
 #              newmodule(w_name) -> w_module
+#                   next(w_iter) -> w_value or raise NoValue
 #

Modified: pypy/trunk/src/pypy/objspace/std/default.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/std/default.py	(original)
+++ pypy/trunk/src/pypy/objspace/std/default.py	Mon Jun 23 16:57:22 2003
@@ -35,10 +35,15 @@
 def not__ANY(space, w_obj):
     return space.newbool(not space.is_true(w_obj))
 
-# everything is True unless otherwise specified
+
+# __nonzero__ falls back to __len__
 
 def is_true__ANY(space, w_obj):
-    return True
+    try:
+        w_len = space.len.perform_call((w_obj,))
+    except FailedToImplement:
+        return True  # everything is True unless otherwise specified
+    return space.is_true(space.ne(w_len, space.newint(0)))
 
 # in-place operators fall back to their non-in-place counterpart
 
@@ -73,8 +78,6 @@
 def issubtype__ANY_ANY(space, w_one, w_two):
     # XXX -- mwh
     return space.newbool(0)
-
-def nonzero__ANY(space, w_obj):
-    return space.newbool(space.is_true(w_obj))
+    # XXX why is it there ? -- Armin
 
 register_all(vars())

Modified: pypy/trunk/src/pypy/objspace/std/objspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/std/objspace.py	(original)
+++ pypy/trunk/src/pypy/objspace/std/objspace.py	Mon Jun 23 16:57:22 2003
@@ -277,6 +277,7 @@
     is_data_descr = MultiMethod('is_data_descr', 1, []) # returns an unwrapped bool
 
     getdict = MultiMethod('getdict', 1, [])  # get '.__dict__' attribute
+    next    = MultiMethod('next', 1, [])     # iterator interface
 
     def is_(self, w_one, w_two):
         # XXX this is a hopefully temporary speed hack:

Modified: pypy/trunk/src/pypy/objspace/std/typeobject.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/std/typeobject.py	(original)
+++ pypy/trunk/src/pypy/objspace/std/typeobject.py	Mon Jun 23 16:57:22 2003
@@ -18,15 +18,6 @@
     def __init__(w_self, space):
         W_Object.__init__(w_self, space)
         w_self.w_tpname = space.wrap(w_self.typename)
-        w_self.multimethods = {}
-        # import all multimethods of the type class and of the objspace
-        for multimethod in (hack_out_multimethods(w_self.__class__) +
-                            hack_out_multimethods(space.__class__)):
-            for i in range(len(multimethod.specialnames)):
-                # each PyMultimethodCode embeds a multimethod
-                name = multimethod.specialnames[i]
-                code = PyMultimethodCode(multimethod, w_self.__class__, i)
-                w_self.multimethods[name] = code
 
     def getbases(w_self):
         parents = w_self.staticbases
@@ -58,10 +49,11 @@
 
     def lookup_exactly_here(w_self, w_key):
         space = w_self.space
+        multimethods = getmultimethods(space.__class__, w_self.__class__)
         key = space.unwrap(w_key)
         assert isinstance(key, str)
         try:
-            code = w_self.multimethods[key]
+            code = multimethods[key]
         except KeyError:
             raise KeyError   # pass on the KeyError
         if code.slice().is_empty():
@@ -83,6 +75,33 @@
             result.append(value)
     return result
 
+AllSlicedMultimethods = {}
+
+def getmultimethods(spaceclass, typeclass):
+    try:
+        multimethods = AllSlicedMultimethods[spaceclass, typeclass]
+    except KeyError:
+        multimethods = AllSlicedMultimethods[spaceclass, typeclass] = {}
+        # import all multimethods of the type class and of the objspace
+        for multimethod in (hack_out_multimethods(typeclass) +
+                            hack_out_multimethods(spaceclass)):
+            for i in range(len(multimethod.specialnames)):
+                # each PyMultimethodCode embeds a multimethod
+                name = multimethod.specialnames[i]
+                if name in multimethods:
+                    # conflict between e.g. __lt__ and
+                    # __lt__-as-reversed-version-of-__gt__
+                    code = multimethods[name]
+                    if code.bound_position < i:
+                        continue
+                code = PyMultimethodCode(multimethod, typeclass, i)
+                multimethods[name] = code
+        # add some more multimethods with a special interface
+        code = NextMultimethodCode(spaceclass.next, typeclass)
+        multimethods['next'] = code
+        code = NonZeroMultimethodCode(spaceclass.is_true, typeclass)
+        multimethods['__nonzero__'] = code
+    return multimethods
 
 class PyMultimethodCode(pycode.PyBaseCode):
 
@@ -104,7 +123,7 @@
     def slice(self):
         return self.basemultimethod.slice(self.typeclass, self.bound_position)
 
-    def eval_code(self, space, w_globals, w_locals):
+    def do_call(self, space, w_globals, w_locals):
         """Call the multimethod, ignoring all implementations that do not
         have exactly the expected type at the bound_position."""
         multimethod = self.slice()
@@ -113,20 +132,38 @@
             w_arg = space.getitem(w_locals, space.wrap('x%d'%(i+1)))
             dispatchargs.append(w_arg)
         dispatchargs = tuple(dispatchargs)
+        return multimethod.get(space).perform_call(dispatchargs)
+
+    def eval_code(self, space, w_globals, w_locals):
+        "Call the multimethods, translating back information to Python."
         try:
-            w_result = multimethod.get(space).perform_call(dispatchargs)
+            w_result = self.do_call(space, w_globals, w_locals)
         except FailedToImplement, e:
             if e.args:
                 raise OperationError(*e.args)
             else:
                 return space.w_NotImplemented
-        except NoValue:
-            raise OperationError(space.w_StopIteration, space.w_None)
-        # XXX hack to accept real Nones from operations with no return value
+        # we accept a real None from operations with no return value
         if w_result is None:
             w_result = space.w_None
         return w_result
 
+class NextMultimethodCode(PyMultimethodCode):
+
+    def eval_code(self, space, w_globals, w_locals):
+        "Call the next() multimethod."
+        try:
+            return self.do_call(space, w_globals, w_locals)
+        except NoValue:
+            raise OperationError(space.w_StopIteration, space.w_None)
+
+class NonZeroMultimethodCode(PyMultimethodCode):
+
+    def eval_code(self, space, w_globals, w_locals):
+        "Call the is_true() multimethods."
+        result = self.do_call(space, w_globals, w_locals)
+        return space.newbool(result)
+
 
 def call__Type_ANY_ANY(space, w_type, w_args, w_kwds):
     w_newobject = space.new(w_type, w_args, w_kwds)

Modified: pypy/trunk/src/pypy/objspace/std/userobject.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/std/userobject.py	(original)
+++ pypy/trunk/src/pypy/objspace/std/userobject.py	Mon Jun 23 16:57:22 2003
@@ -100,51 +100,72 @@
     else:
         return 1        
 
-# register an implementation for all multimethods that define special names
-def user_specialmethod(space, *args_w):
-    # args_w is in the standard multimethod order
-    # we need it in the Python-friendly order (i.e. swapped for __rxxx__)
-    args_w = list(args_w)
-    w_userobj = args_w.pop(g_bound_position)
-    w_args = space.newtuple(args_w)
-    w_key = space.wrap(g_method_name)
-    mro = space.getattr(w_userobj.w_type, space.wrap('__mro__'))
-    mro = space.unpacktuple(mro)
-    for w_base in mro:
-        if not isinstance(w_base, W_UserType):
-            continue
-        try:
-            w_function = w_base.lookup_exactly_here(w_key)
-        except KeyError:
-            continue
-        w_method = space.get(w_function, w_userobj, w_base)
-        w_result = space.call(w_method, w_args, space.newdict([]))
-        # XXX hack to accept real Nones from operations with no return value
-        if w_result is None:
-            return space.w_None
-        elif space.is_true(space.is_(w_result, space.w_NotImplemented)):
+
+class SpecialMethod:
+    """An implementation for a multimethod that looks for a user-defined
+    special __xxx__ method."""
+
+    def __init__(self, method_name, bound_position=0):
+        self.method_name = method_name
+        self.bound_position = bound_position
+
+    def do_call(self, space, args_w):
+        # args_w is in the standard multimethod order
+        # we need it in the Python-friendly order (i.e. swapped for __rxxx__)
+        args_w = list(args_w)
+        w_userobj = args_w.pop(self.bound_position)
+        w_args = space.newtuple(args_w)
+        w_key = space.wrap(self.method_name)
+        w_mro = space.getattr(w_userobj.w_type, space.wrap('__mro__'))
+        mro = space.unpacktuple(w_mro)
+        for w_base in mro:
+            if not isinstance(w_base, W_UserType):
+                continue
+            try:
+                w_function = w_base.lookup_exactly_here(w_key)
+            except KeyError:
+                continue
+            w_method = space.get(w_function, w_userobj, w_base)
+            return space.call(w_method, w_args, space.newdict([]))
+        raise FailedToImplement
+
+    def normal_call(self, space, *args_w):
+        "Call a user-defined __xxx__ method and convert the result back."
+        w_result = self.do_call(space, args_w)
+        # interpret 'NotImplemented' as meaning not implemented (duh).
+        if space.is_true(space.is_(w_result, space.w_NotImplemented)):
             raise FailedToImplement
-        else:
-            return w_result
-    raise FailedToImplement
+        return w_result
+
+    def next_call(self, space, *args_w):
+        "For .next()."
+        # don't accept NotImplemented nor a real None, but catch StopIteration
+        try:
+            return self.do_call(space, args_w)
+        except OperationError, e:
+            if not e.match(self.space, self.space.w_StopIteration):
+                raise
+            raise NoValue
+
+    def nonzero_call(self, space, *args_w):
+        "For __nonzero__()."
+        # accept any object and return its truth value
+        # XXX if the user returns another custom object he can
+        #     force the interpreter into an infinite loop
+        w_result = self.do_call(space, args_w)
+        return space.is_true(w_result)
+
 
 import new
 for multimethod in typeobject.hack_out_multimethods(StdObjSpace):
     for i in range(len(multimethod.specialnames)):
-        # a hack to avoid nested scopes is to give the function
-        # a custom globals dictionary
-
-        g = {'W_UserType'       : W_UserType,
-             'FailedToImplement': FailedToImplement,
-             '__builtins__'     : __builtins__,
-             'g_method_name'    : multimethod.specialnames[i],
-             'g_bound_position' : i}
-        f = new.function(user_specialmethod.func_code, g,
-                         'user_%s' % multimethod.specialnames[i])
-
+        f = SpecialMethod(multimethod.specialnames[i], i).normal_call
         signature = [W_ANY] * multimethod.arity
         signature[i] = W_UserObject
         multimethod.register(f, *signature)
 
+next__User    = SpecialMethod('next').next_call
+is_true__User = SpecialMethod('nonzero').nonzero_call
+
 
 register_all(vars())


More information about the Pypy-commit mailing list