comparing with /Users/robert/Desktop/getattr_support.hg
searching for changes
changeset:   492:e0b4c3db1245
parent:      483:b869698d6f22
user:        Peter Todd <pete@petertodd.org>
date:        Fri May 02 04:22:48 2008 -0400
summary:     First stage of __getattribute__ special method support.

diff -r b869698d6f22 -r e0b4c3db1245 Cython/Compiler/ModuleNode.py
--- a/Cython/Compiler/ModuleNode.py	Thu May 01 20:09:34 2008 +0200
+++ b/Cython/Compiler/ModuleNode.py	Fri May 02 04:22:48 2008 -0400
@@ -666,7 +666,7 @@ class ModuleNode(Nodes.Node, Nodes.Block
                         self.generate_ass_subscript_function(scope, code)
                     if scope.defines_any(["__setslice__", "__delslice__"]):
                         self.generate_ass_slice_function(scope, code)
-                    if scope.defines_any(["__getattr__"]):
+                    if scope.defines_any(["__getattr__","__getattribute__"]):
                         self.generate_getattro_function(scope, code)
                     if scope.defines_any(["__setattr__", "__delattr__"]):
                         self.generate_setattro_function(scope, code)
@@ -1030,27 +1030,36 @@ class ModuleNode(Nodes.Node, Nodes.Block
             "}")
 
     def generate_getattro_function(self, scope, code):
-        # First try to get the attribute using PyObject_GenericGetAttr.
+        # First try to get the attribute using __getattribute__, if defined, or
+        # PyObject_GenericGetAttr.
+        #
         # If that raises an AttributeError, call the user's __getattr__
-        # method.
-        entry = scope.lookup_here("__getattr__")
+        # method, if defined.
+        getattr_entry = scope.lookup_here("__getattr__")
+        getattribute_entry = scope.lookup_here("__getattribute__")
         code.putln("")
         code.putln(
             "static PyObject *%s(PyObject *o, PyObject *n) {"
                 % scope.mangle_internal("tp_getattro"))
-        code.putln(
+        if getattribute_entry is not None:
+            code.putln(
+                "PyObject *v = %s(o, n);" %
+                    getattribute_entry.func_cname)
+        else:
+            code.putln(
                 "PyObject *v = PyObject_GenericGetAttr(o, n);")
-        code.putln(
+        if getattr_entry is not None:
+            code.putln(
                 "if (!v && PyErr_ExceptionMatches(PyExc_AttributeError)) {")
-        code.putln(
-                    "PyErr_Clear();")
-        code.putln(
-                    "v = %s(o, n);" %
-                        entry.func_cname)
-        code.putln(
+            code.putln(
+                "PyErr_Clear();")
+            code.putln(
+                "v = %s(o, n);" %
+                    getattr_entry.func_cname)
+            code.putln(
                 "}")
         code.putln(
-                "return v;")
+            "return v;")
         code.putln(
             "}")
     
diff -r b869698d6f22 -r e0b4c3db1245 Cython/Compiler/TypeSlots.py
--- a/Cython/Compiler/TypeSlots.py	Thu May 01 20:09:34 2008 +0200
+++ b/Cython/Compiler/TypeSlots.py	Fri May 02 04:22:48 2008 -0400
@@ -610,7 +610,7 @@ slot_table = (
     MethodSlot(callfunc, "tp_call", "__call__"),
     MethodSlot(reprfunc, "tp_str", "__str__"),
     
-    SyntheticSlot("tp_getattro", ["__getattr__"], "0"), #"PyObject_GenericGetAttr"),
+    SyntheticSlot("tp_getattro", ["__getattr__","__getattribute__"], "0"), #"PyObject_GenericGetAttr"),
     SyntheticSlot("tp_setattro", ["__setattr__", "__delattr__"], "0"), #"PyObject_GenericSetAttr"),
 
     SuiteSlot(PyBufferProcs, "PyBufferProcs", "tp_as_buffer"),
diff -r b869698d6f22 -r e0b4c3db1245 tests/run/__getattribute__.pyx
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/run/__getattribute__.pyx	Fri May 02 04:22:48 2008 -0400
@@ -0,0 +1,60 @@
+__doc__ = """
+__getattribute__ and __getattr__ special methods for a single class.
+
+    >>> a = just_getattribute()
+    >>> a.bar
+    'bar'
+    >>> a.invalid
+    Traceback (most recent call last):
+    AttributeError
+
+    >>> a = just_getattr()
+    >>> a.foo
+    10
+    >>> a.bar
+    'bar'
+    >>> a.invalid
+    Traceback (most recent call last):
+    AttributeError
+
+    >>> a = both()
+    >>> a.foo
+    10
+    >>> a.bar
+    'bar'
+    >>> a.invalid
+    Traceback (most recent call last):
+    AttributeError
+"""
+
+cdef class just_getattribute:
+    def __getattribute__(self,n):
+        if n == 'bar':
+            return n
+        else:
+            raise AttributeError
+
+cdef class just_getattr:
+    cdef readonly int foo
+    def __init__(self):
+        self.foo = 10
+    def __getattr__(self,n):
+        if n == 'bar':
+            return n
+        else:
+            raise AttributeError
+
+cdef class both:
+    cdef readonly int foo
+    def __init__(self):
+        self.foo = 10
+    def __getattribute__(self,n):
+        if n == 'foo':
+            return self.foo
+        else:
+            raise AttributeError
+    def __getattr__(self,n):
+        if n == 'bar':
+            return n
+        else:
+            raise AttributeError

changeset:   493:8582cdefc1f4
tag:         tip
user:        Peter Todd <pete@petertodd.org>
date:        Sun May 04 23:28:40 2008 -0400
summary:     __getattr(ibute)__ special methods now work with subclasses.

diff -r e0b4c3db1245 -r 8582cdefc1f4 Cython/Compiler/ModuleNode.py
--- a/Cython/Compiler/ModuleNode.py	Fri May 02 04:22:48 2008 -0400
+++ b/Cython/Compiler/ModuleNode.py	Sun May 04 23:28:40 2008 -0400
@@ -1033,10 +1033,21 @@ class ModuleNode(Nodes.Node, Nodes.Block
         # First try to get the attribute using __getattribute__, if defined, or
         # PyObject_GenericGetAttr.
         #
-        # If that raises an AttributeError, call the user's __getattr__
-        # method, if defined.
-        getattr_entry = scope.lookup_here("__getattr__")
-        getattribute_entry = scope.lookup_here("__getattribute__")
+        # If that raises an AttributeError, call the __getattr__ if defined.
+        #
+        # In both cases, defined can be in this class, or any base class.
+        def lookup_here_or_base(n,type=None):
+            # Recursive lookup
+            if type is None:
+                type = scope.parent_type
+            r = type.scope.lookup_here(n)
+            if r is None and \
+               type.base_type is not None:
+                return lookup_here_or_base(n,type.base_type)
+            else:
+                return r
+        getattr_entry = lookup_here_or_base("__getattr__")
+        getattribute_entry = lookup_here_or_base("__getattribute__")
         code.putln("")
         code.putln(
             "static PyObject *%s(PyObject *o, PyObject *n) {"
diff -r e0b4c3db1245 -r 8582cdefc1f4 tests/run/__getattribute_subclasses__.pyx
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/run/__getattribute_subclasses__.pyx	Sun May 04 23:28:40 2008 -0400
@@ -0,0 +1,126 @@
+__doc__ = """
+__getattribute__ and __getattr__ special methods and subclasses. 
+
+getattr does not override members. 
+    >>> a = getattr_boring()
+    >>> a.boring_member
+    10
+    >>> a.resolved_by
+    'getattr_boring'
+
+getattribute does.
+    >>> a = getattribute_boring()
+    >>> a.boring_member
+    Traceback (most recent call last):
+    AttributeError
+    >>> a.resolved_by
+    'getattribute_boring'
+
+Is inherited.
+    >>> a = boring_boring_getattribute()
+    >>> a.boring_getattribute_member
+    Traceback (most recent call last):
+    AttributeError
+    >>> a.boring_boring_getattribute_member
+    Traceback (most recent call last):
+    AttributeError
+    >>> a.resolved_by
+    '_getattribute'
+
+__getattribute__ is always tried first, then __getattr__, regardless of where
+in the inheritance hiarchy they came from.
+    >>> a = getattribute_boring_boring_getattr()
+    >>> a.foo
+    Traceback (most recent call last):
+    AttributeError
+    >>> a.resolved_by
+    'getattribute_boring_boring_getattr'
+    >>> a.getattribute_boring_boring_getattr
+    True
+    >>> a._getattr
+    True
+
+    >>> a = getattr_boring_boring_getattribute()
+    >>> a.foo
+    Traceback (most recent call last):
+    AttributeError
+    >>> a.resolved_by
+    '_getattribute'
+    >>> a.getattr_boring_boring_getattribute
+    True
+    >>> a._getattribute
+    True
+
+"""
+
+cdef class boring:
+    cdef readonly int boring_member 
+    def __init__(self):
+        self.boring_member = 10
+
+cdef class getattr_boring(boring):
+    def __getattr__(self,n):
+        if n == 'resolved_by':
+            return 'getattr_boring'
+        elif n == 'getattr_boring':
+            return True
+        else:
+            raise AttributeError
+
+cdef class getattribute_boring(boring):
+    def __getattribute__(self,n):
+        if n == 'resolved_by':
+            return 'getattribute_boring'
+        elif n == 'getattribute_boring':
+            return True
+        else:
+            raise AttributeError
+
+cdef class _getattr:
+    def __getattr__(self,n):
+        if n == 'resolved_by':
+            return '_getattr'
+        elif n == '_getattr':
+            return True
+        else:
+            raise AttributeError
+
+cdef class _getattribute(boring):
+    def __getattribute__(self,n):
+        if n == 'resolved_by':
+            return '_getattribute'
+        elif n == '_getattribute':
+            return True
+        else:
+            raise AttributeError
+
+cdef class boring_getattribute(_getattribute):
+    cdef readonly int boring_getattribute_member
+
+cdef class boring_boring_getattribute(boring_getattribute):
+    cdef readonly int boring_boring_getattribute_member 
+
+cdef class boring_getattr(_getattr):
+    cdef readonly int boring_getattr_member
+
+cdef class boring_boring_getattr(boring_getattr):
+    cdef readonly int boring_boring_getattr_member 
+
+cdef class getattribute_boring_boring_getattr(boring_boring_getattr):
+    def __getattribute__(self,n):
+        if n == 'resolved_by':
+            return 'getattribute_boring_boring_getattr'
+        elif n == 'getattribute_boring_boring_getattr':
+            return True
+        else:
+            raise AttributeError
+
+cdef class getattr_boring_boring_getattribute(boring_boring_getattribute):
+    def __getattr__(self,n):
+        if n == 'resolved_by':
+            return 'getattr_boring_boring_getattribute'
+        elif n == 'getattr_boring_boring_getattribute':
+            return True
+        else:
+            raise AttributeError
+

